import requests
import re
import json
import random
from frame_work.excel import get_all_data
from frame_work.util import (
    get_sql_info,
    check_tid,
    change_user_account_status,
    bind_tb,
    assert_result_dict,
    get_str
)
from frame_work.logger import Logger
from frame_work.login import PcLogin, AppLogin
from frame_work import menu_list
from requests_toolbelt import MultipartEncoder
from multiprocessing import Process, Pool
from frame_work.setting import *
from frame_work.yaml_file import YAML

logger = Logger(__name__).get_logger()


class Apply(object):
    # 定义申请成功数量初始值
    init_sum = 0

    def __init__(self, tid):
        self.tid = tid
        self.session = ''
        self.yaml = YAML(os.path.join(base_Dir, 'data', 'data.yaml'))

    def get_activity_apply_info(self):
        """
        查询申请活动所必须的活动信息
        :return:
        """
        statement = """SELECT
                        t.level,t.type,t.url_order,tb.answer
                    FROM
                        sk_try t
                    LEFT JOIN sk_try_type_browse tb ON t.tid = tb.tid
                    WHERE
                        t.tid = %s"""
        params = (self.tid,)
        value = get_sql_info(statement, params, meta={
            "info": "查询申请活动所必须的活动等级Level/type/url_order/answer"
        })
        return value[0]

    @staticmethod
    def get_user_data(nums):
        """
        批量读取/data/buyer_data.xlsx试客账户，达到申请数量目的
        :param nums:
        :return:
        """
        data = get_all_data(1)
        # 打乱user_data里边的数据
        random.shuffle(data)
        user_data = []
        for uname in data:
            if nums == len(user_data):
                break
            # 如果用户状态不对，改为正常
            account_status = change_user_account_status(uname)
            # 没绑淘宝就开始绑订
            tb_status = bind_tb(uname)
            if account_status and tb_status:
                user_data.append(uname)
            continue
        return user_data

    def get_pic(self, title):
        """
        发布活动中会含有上传图片的操作，此函数在作用post一张图片到图片服务器，获取响应值类似：tmp/2019/08/20/08500820924072552490.jpg
        :param num:第几张图片
        :param title:活动类型与来源的拼接
        :return:pic_addr
        """
        yaml_data = self.yaml.get_data('task_apply_pic')
        yaml_data.get('data').update({
            'name': title,
            'Filedata': ('filename', open(os.path.join(image_Path, title), 'rb'), 'image/jpg')
        })
        m = MultipartEncoder(
            fields=yaml_data.get('data')
        )
        r = self.session.post(url=yaml_data.get('url'), data=m,
                              headers={'Content-Type': m.content_type})
        return r.text

    @staticmethod
    def sum(res):
        # 批量申请累计数量 res为异步回调结果
        if res:
            Apply.init_sum += 1
        return Apply.init_sum

    @staticmethod
    def multi_async_apply(f, *args, **kwargs):
        """
        多进程异步执行批量申请
        :param f: 目标待执行函数
        :param args:
        :param kwargs:
        :return:
        """
        p = Pool(2)
        # # 初始赋值
        # Apply.init_sum = 0
        for uname in args:
            # 多进程异步执行申请 根据flag来确定执行免拍还是PC/APP申请
            if not kwargs.get('flag'):
                # 免拍
                res = p.apply_async(func=f, args=(uname,), callback=Apply.sum)
            else:
                # PC/APP申请
                res = p.apply_async(
                    func=f,
                    args=(
                        uname,
                        kwargs.get('flag'),
                    ),
                    callback=Apply.sum)
        # 不在接收任何进程
        p.close()
        # 等待所有子进程结束
        p.join()
        if kwargs.get('num') == Apply.init_sum:
            logger.info("达到申请成功的数量:%s" % Apply.init_sum)
        else:
            logger.info(
                "还差: %s个未申请成功,可能是账户问题,请单个执行！" %
                (kwargs.get('num') - Apply.init_sum))


class PcApply(Apply):

    # PC活动申请
    def __init__(self, tid):
        super().__init__(tid)
        self.info = self.get_activity_apply_info()

    @staticmethod
    def get_session(uname):
        # 返回登录session
        login = PcLogin(uname)
        login.pc_login()
        return login.get_session()

    def get_cache_key(self, url):
        """
        该方法目的就是获取cache_key值,因为申请试用需要带这个参数
        :return:
        """
        # 获取cache_key
        getkey = self.session.get(url).text
        try:
            cache_key = re.findall(r'"key":"(.*?)"', getkey)[0]
            logger.debug('获取缓存Key值 => %s...' % cache_key)
        except (IndexError, KeyError) as e:
            logger.error('获取cache_key失败，请检查response中该key:value键值是否改变了')
            return False
        else:
            return cache_key

    def apply(self, uname, flag=1):
        """
        PC申请
        :param uname:
        :param flag:
        :return:
        """
        self.session = PcApply.get_session(uname)
        level = self.info.get("level")
        type = self.info.get("type")
        # acti_level = 'detail' if level==1 else 'platinum' # 根据商家级别来判断url的前缀
        yaml_apply_data = self.yaml.get_data(
            'detail_url') if level == 1 else self.yaml.get_data('platinum_url')  # 根据商家级别来判断申请data
        yaml_cache_data = self.yaml.get_data('detail_cache_key') if level == 1 else self.yaml.get_data(
            'platinum_cache_key')  # 根据商家级别来判断获取cache_key data
        yaml_coin_data = self.yaml.get_data('detail_coin_url') if level == 1 else self.yaml.get_data(
            'platinum_coin_url')  # 根据商家级别来判断pc兑换券申请data
        cache_key = self.get_cache_key(
            yaml_cache_data['url'] + '/' + str(self.tid) + '/')
        if type == 4:
            answer = self.info.get("answer")
            yaml_apply_data['data'].update({
                'cache_key': cache_key,
                'answer': answer
            })
        elif type == 2:
            url_order = self.info.get("url_order")
            yaml_apply_data['data'].update({
                'cache_key': cache_key,
                'url_order': url_order
            })
        elif type == 14:
            pic_1 = self.get_pic("任务图片1.jpg")
            pic_2 = self.get_pic("任务图片2.jpg")
            pic_vale_1 = json.loads(pic_1).get("data")
            pic_vale_2 = json.loads(pic_2).get("data")
            yaml_apply_data['data'].update({
                'cache_key': cache_key,
                'task_img1': pic_vale_1,
                'task_img2': pic_vale_2
            })
        else:
            yaml_apply_data['data'].update({
                'cache_key': cache_key
            })
        logger.debug('开始申请试用资格')
        if flag != 1:
            response = self.session.post(
                yaml_coin_data['url'] + '/' + str(
                    self.tid),
                data=yaml_apply_data.get('data'),
                headers=yaml_coin_data.get('header')).text
        else:
            response = self.session.post(
                yaml_apply_data['url'] + '/' + str(
                    self.tid) + '?callback=trysapply',
                data=yaml_apply_data.get('data'),
                headers=yaml_apply_data.get('header')).text
        try:
            if "trysapply(',')" in response:
                res_str = get_str(response, 'trysapply(', ')')
            else:
                res_str = response
        except Exception as e:
            logger.debug('响应值处理失败:%s 响应内容：%s' % (e, response))
            return False
        else:
            assert_result = assert_result_dict(
                res_str, uname + '申请tid:' + str(self.tid) + '活动')
            logger.debug('申请结束')
            return assert_result


class AppApply(Apply):

    # APP活动申请
    def __init__(self, tid):
        Apply.__init__(self, tid)

    @staticmethod
    def get_session(uname):
        # 返回登录session
        login = AppLogin(uname)
        sign = login.appget_sign()
        return sign

    def app_apply(self, uname, type=1):
        """
        app端兑换券申请或者直接申请
        :param uname:
        :param type:
        :return:
        """
        statement = """select uid from sk_user where uname = %s"""
        params = (uname,)
        value = get_sql_info(statement, params, meta={
            "info": "查询uid"
        })
        sign = AppApply.get_session(uname)
        if sign:
            yaml_data = self.yaml.get_data('app_apply')
            # type=2app兑换券申请
            if type == 2:
                logger.debug('开始app使用兑换券申请试用资格')
                yaml_data.get('data').update({'uid': value[0].get("uid"),
                                              'tid': self.tid,
                                              'sign': sign,
                                              'use_coin': 1
                                              })
            # type=1 app申请
            else:
                logger.debug('开始app申请试用资格')
                yaml_data.get('data').update({'uid': value[0].get("uid"),
                                              'tid': self.tid,
                                              'sign': sign
                                              })
            response = requests.post(
                yaml_data.get('url'),
                data=yaml_data.get('data'),
                headers=yaml_data.get('header')).text
            status = assert_result_dict(
                response, uname + '申请tid:' + str(self.tid) + '活动')
            return status
        else:
            logger.debug('APP登录失败')

# class MpApply(Apply):
#
#     # 免拍试用活动申领
#     def __init__(self,tid):
#         Apply.__init__(self,tid)
#
#     @staticmethod
#     def get_session(uname):
#         # 返回登录session
#         login = AppLogin(uname)
#         sign = login.appget_sign()
#         return sign
#
#     def get_user_address_id(self,uid,sign,pid):
#         """
#         获取用户地址id
#         :param uid:
#         :param sign:
#         :param pid:
#         :return:
#         """
#         mp_address_url = global_config.get_url('mp_address_url')
#         headers = public.header(2)
#         if sign:
#             post_data = public.post_data(55,uid=uid,id=pid,sign=sign)
#             response = requests.post(mp_address_url, data=post_data, headers=headers).text
#             response_json = json.loads(response)
#             if response_json["code"] != "OK":
#                 return
#             address_id = response_json["data"][0].get("id")
#             return address_id
#         else:
#             logger.debug ('APP登录失败')
#             return
#
#     def app_mp(self,uname):
#         """
#         免拍试用申领
#         :param uname:
#         :return:
#         """
#         mp_url = global_config.get_url('mp_url')
#         header = public.header(2)
#         statement = """SELECT id from sk_pur where out_pur_id =%s"""
#         params = (self.tid,)
#         value = get_sql_info(statement,params,meta={
#                 "info":"获取活动唯一编号pid"
#                     })
#         pid = value[0].get("id")
#         if pid:
#             statement = """select uid from sk_user where uname = %s"""
#             params = (uname,)
#             value = get_sql_info(statement,params,meta={
#                 "info":"查询uid"
#                     })
#             uid = value[0].get("uid")
#             sign = MpApply.get_session(uname)
#             addresses = get_user_address(sign,uid)
#             if not addresses:
#                 logger.debug("该试客%s未绑定收货地址,后台自动绑定" % uname)
#                 bind_address(sign,uname,uid)
#             addrid = self.get_user_address_id(uid,sign,pid)
#             if not addrid:
#                 return
#             logger.debug ('开始免拍试用申领')
#             post_data = public.post_data(54,uid=value[0].get("uid"),purid=pid,sign=sign,address_id = addrid)
#             response = requests.post(mp_url, data=post_data, headers=header).text
#             status = assert_result_dict(response,uname+'申请免拍tid:'+str(self.tid)+'活动')
#             return status
#         else:
#             logger.error('sk_pur活动表没有该活动')


def main():
    while True:
        # 选择功能菜单
        menu_list.apply_menu()
        # 选择
        choise = input("请输入您的选择: ")
        if choise in ('q', 'Q'):
            break
        try:
            tid = int(input(u'请输入活动tid: '))
            if choise not in ('3', '8'):
                status = check_tid(tid)
                if not status:
                    raise Exception("活动tid不存在try表，请核实后在重新输入")
        except Exception as e:
            logger.error("reszon => %s" % e)
        else:
            if choise == '1':
                # 统一
                uname = input('请输入您指定的试客账户名: ')
                apply_obj = PcApply(tid)
                apply_obj.apply(uname, 1)
            elif choise == '2':
                # type=1为app直接申请
                # app端申请，需要在app端做登录以获取申请接口参数sign值
                uname = input('请输入您指定的试客账户名: ')
                app_apply_obj = AppApply(tid)
                app_apply_obj.app_apply(uname, 1)
            elif choise == '3':
                # 免拍申请
                # uname = input('请输入您指定的试客账户名: ')
                # mp_apply_obj = MpApply(tid)
                # mp_apply_obj.app_mp(uname)
                pass
            elif choise == '4':
                # pc兑换券申请
                uname = input('请输入您指定的试客账户名: ')
                apply_obj = PcApply(tid)
                apply_obj.apply(uname, 2)
            elif choise == '5':
                # type=2为app兑换券申请
                # app端申请，需要在app端做登录以获取申请接口参数sign值
                uname = input('请输入您指定的试客账户名: ')
                app_apply_obj = AppApply(tid)
                app_apply_obj.app_apply(uname, 2)
            elif choise == '6':
                # 批量PC申请
                num = int(input(u'请输入批量申请数量: '))
                apply_obj = PcApply(tid)
                user_datas = apply_obj.get_user_data(num)
                apply_obj.multi_async_apply(
                    apply_obj.apply, *user_datas, num=num, flag=1)
            elif choise == '7':
                # 批量app申请
                num = int(input(u'请输入批量申请数量: '))
                app_apply_obj = AppApply(tid)
                user_datas = app_apply_obj.get_user_data(num)
                app_apply_obj.multi_async_apply(
                    app_apply_obj.app_apply, *user_datas, num=num, flag=1)
            elif choise == '8':
                # 批量免拍申请
                #     num = int(input(u'请输入批量申请数量: '))
                #     mp_apply_obj = MpApply(tid)
                #     user_datas = mp_apply_obj.get_user_data(num)
                #     mp_apply_obj.multi_async_apply(mp_apply_obj.app_mp,*user_datas,num=num)
                pass
            else:
                logger.info("没有对应的选项,请按选项输入...")
                break


if __name__ == '__main__':
    main()
